package org.testis.gui.mvc.controller.testdesign;

import org.testis.gui.mvc.controller.*;
import org.testis.gui.mvc.model.testdesign.CompareGUIModel;
import org.testis.gui.mvc.utilities.ComparisonTreeNode;
import org.testis.gui.mvc.utilities.Operation;
import org.testis.gui.mvc.utilities.OperationExecutor;
import org.testis.gui.mvc.view.testdesign.CompareGUI;
import org.testis.utils.tree.MyDefaultMutableTreeNode;
import java.util.LinkedList;

import org.testis.orm.refinedclasses.AtofComparable;
import org.testis.utils.tree.TreeNode;

/**
 *
 * @author emir
 */
public class CompareGUIController extends AbstractController {

    public static final String COPY = Operation.COPY;
    public static final String CLONE = Operation.CLONE;
    public static final String DEFAULT_OPERATION_MODE = CLONE;
    public static final String SELECT_MODE = "OperationMode";
    public static final String SRC_TREE_SRC_REFRESH = "RefreshSrcTree";
    public static final String DST_TREE_REFRESH = "RefreshDstTree";
    public static final String SELECT_SOURCE_NODE = "SelectedSourceNode";
    public static final String SELECT_DESTINATION_NODE = "SelectedDestinationNode";
    public static final String SHOW = "ShowWindow";
    public static final String SOURCE = "Source";
    public static final String DESTINATION = "Destination";
    public static final String PROPERTIES = "Properties";
    public static final String OPERATIONS = "Operations";
    public static final String SRC_LABEL = "SourceLabel";
    public static final String DST_LABEL = "DestinationLabel";
    public static final String ADD_OPERATION = "AddOperation";
    public static final String REMOVE_OPERATION = "RemoveOperation";
    public static final String HIGHLIGHT_NODE = "Highlight";
    public static final String UN_HIGHLIGHT_NODE = "UnHighlight";
    public static final String SINGLE_MODE = "SingleMode";
    public static final String EXECUTOR = "Executor";

    public CompareGUIController() {
        super();
    }

    public void start(MyDefaultMutableTreeNode src, MyDefaultMutableTreeNode dst, OperationExecutor executor) {
        System.out.println("COMPARE LAUNCHED");
        if (src != null && dst != null) {
            initialize();
            setModelProperty(SOURCE, createGUICopy(src));
            setModelProperty(DESTINATION, createGUICopy(dst));
            setModelProperty(EXECUTOR, executor);
            interpret();
            //operationRefreshSourceTree();
            //operationRefreshDestinationTree();
            setLabels(src, dst);
            setModelProperty(SHOW,
                    true);
        }
    }

    public void operationSetSingleMode(Boolean mode) {
        setModelProperty(SINGLE_MODE, mode);
    }

    public boolean operationGetSingleMode() {
        return (Boolean) getModelProperty(SINGLE_MODE);
    }

    public void clickOk() {
        LinkedList<Operation> operations = new LinkedList<Operation>();
        getOperationList(operationGetDestination(), operations);
        execute(operations);
    }

    private LinkedList<Operation> getOperationList(ComparisonTreeNode rootTree, LinkedList<Operation> operations) {
        if (rootTree.getGuiReference() != null) {
            if (rootTree.getGuiReference().getUserObject() instanceof Operation) {
                operations.add((Operation) rootTree.getGuiReference().getUserObject());
            }
        }
        for (int i = 0; i < rootTree.getChildCount(); i++) {
            getOperationList(rootTree.getChildAt(i), operations);
        }
        return operations;
    }

    private ComparisonTreeNode operationGetDestination() {
        return (ComparisonTreeNode) getModelProperty(DESTINATION);
    }

    public void addOperations() {
        addOperations(operationGetSelectedSourceNode());
        operationRefreshDestinationTree();
    }

    public void addOperations(ComparisonTreeNode node) {
        LinkedList<Operation> operationList = node.getOperations();
        boolean foundExact;
        int count = 0;
        for (int i = 0; i < operationList.size() && ((operationGetSingleMode() && count < 1) || !operationGetSingleMode()); i++) {
            foundExact = false;
            Operation operationToAdd = operationList.get(i);
            if (node.getSelectedOperations().contains(operationToAdd)) {
                for (int j = 0; j < operationToAdd.getRemoveFromNode().getChildCount(); j++) {
                    if (operationToAdd.getRemoveFromNode().getChildAt(j).getUserObject().equals(operationToAdd)) {
                        //removeOperation(operationToAdd.getRemoveFromNode().getChildAt(j));
                        //break;
                        foundExact = true;
                        break;
                    }
                }
            } else {
                AtofComparable src;
                AtofComparable dst;
                src = (AtofComparable) operationToAdd.getSource().getUserObject();
                LinkedList<Operation> destinationsSelectedOperations = operationToAdd.getRemoveFromNode().getSelectedOperations();
                for (int j = 0; j < destinationsSelectedOperations.size(); j++) {
                    dst = (AtofComparable) destinationsSelectedOperations.get(j).getSource().getUserObject();
                    if (dst.compare(src, null) == AtofComparable.EXACT) {
                        foundExact = true;
                        break;
                    }
                }
            }
            if (foundExact) {
                continue;
            }
            operationToAdd.setOperation(getMode());
            operationToAdd.getRemoveFromNode().addOperation(operationToAdd);
            ComparisonTreeNode newNode = new ComparisonTreeNode(new MyDefaultMutableTreeNode(operationToAdd, false), false);
            newNode.setColor(ComparisonTreeNode.COMPARE);
            operationToAdd.getRemoveFromNode().add(newNode);
            operationToAdd.getRemoveFromNode().addSelectedOperation(operationToAdd);
            node.addSelectedOperation(operationToAdd);
            setModelProperty(ADD_OPERATION, newNode);
            count++;
        }
    }

    public void removeOperation() {
        removeOperation(operationGetSelectedDestinationNode());
        operationRefreshDestinationTree();
    }

    private void removeOperation(ComparisonTreeNode node) {
        Operation operationToRemove = (Operation) node.getUserObject();
        node.getParent().removeSelectedOperation(operationToRemove);
        operationToRemove.getAddToNode().removeSelectedOperation(operationToRemove);
        node.removeFromParent();
        setModelProperty(REMOVE_OPERATION, node);
    }

    private void setLabels(MyDefaultMutableTreeNode src, MyDefaultMutableTreeNode dst) {
        if (src != null && dst != null) {
            TreeNode node = (TreeNode) src.getUserObject();
            while (node.getParent() != null) {
                node = node.getParent();
            }
            setModelProperty(SRC_LABEL, node.getName());
            node = (TreeNode) dst.getUserObject();
            while (node.getParent() != null) {
                node = node.getParent();
            }
            setModelProperty(DST_LABEL, node.getName());
        }
    }

    private void execute(LinkedList<Operation> operations) {
        for (int i = 0; i < operations.size(); i++) {
            operations.get(i).execute();
        }

    }

    public String getMode() {
        return (String) getModelProperty(SELECT_MODE);
    }

    private void interpret() {
        ComparisonTreeNode src = getSource();
        ComparisonTreeNode dst = getDestination();
        compare(src, dst);
        markUnique(dst);
        markUnique(src);
    }

    private void markUnique(ComparisonTreeNode dst) {
        if (!dst.isSelected()) {
            dst.setColor(ComparisonTreeNode.DIFF_NAME);
        }
        for (int i = 0; i < dst.getChildCount(); i++) {
            markUnique(dst.getChildAt(i));
        }
    }

    private boolean compare(ComparisonTreeNode src, ComparisonTreeNode dst) {
        int result = dst.compare(src);
        if (result == AtofComparable.DIFFERENT_NAME) {
            return false;
        } else {
            src.setSelectedSingle(true);
            dst.setSelectedSingle(true);
            if (result != AtofComparable.EXACT) {
                OperationExecutor executor = (OperationExecutor) getModelProperty(EXECUTOR);
                Operation operationToAdd = new Operation(Operation.REPLACE, src, dst, executor);
                src.addOperation(operationToAdd);
                dst.addOperation(operationToAdd);
            }
        }
        for (int i = 0; i < src.getChildCount(); i++) {
            boolean found = false;
            for (int j = 0; j < dst.getChildCount(); j++) {
                boolean newResult = compare(src.getChildAt(i), dst.getChildAt(j));
                found = newResult || found;
            }
            if (!found) {
                OperationExecutor executor = (OperationExecutor) getModelProperty(EXECUTOR);
                Operation operationToAdd = new Operation(null, src.getChildAt(i), dst, executor);
                src.getChildAt(i).addOperation(operationToAdd);
                dst.addOperation(operationToAdd);
            }
        }
        return true;
    }

    private ComparisonTreeNode createGUICopy(MyDefaultMutableTreeNode node) {
        ComparisonTreeNode result = new ComparisonTreeNode(node, false);
        for (int i = 0; i < node.getChildCount(); i++) {
            result.add(createGUICopy(node.getChildAt(i)));
        }
        return result;
    }

    public void operationSelectSourceNode(ComparisonTreeNode node) {
        operationUnHighlightNode(operationGetSelectedDestinationNode());
        operationUnHighlightNode(operationGetSelectedSourceNode());
        setModelProperty(SELECT_DESTINATION_NODE, null);
        setModelProperty(SELECT_SOURCE_NODE, node);
        if (node != null) {
            operationSetProperties(node.getUserObject().toString());
            operationHighlightNode(node);
        } else {
            operationSetProperties("");
        }
    }

    private void operationHighlightNode(MyDefaultMutableTreeNode node) {
        setModelProperty(HIGHLIGHT_NODE, node);
        TestDesignControllerList.newInstance().getTreeController().operationRefreshTree();
        DynamicMapControllerList.newInstance().getDynamicMapTreeController().operationRefreshTree();
    }

    public void operationHighlightNode(ComparisonTreeNode node) {
        if (node != null) {
            setModelProperty(HIGHLIGHT_NODE, node);
            operationHighlightNode(node.getGuiReference());
            LinkedList<Operation> operationList = node.getOperations();
            LinkedList<Operation> operationSelectedList = node.getSelectedOperations();
            for (int i = 0; i < operationList.size(); i++) {
                if (!operationSelectedList.contains(operationList.get(i))) {
                    setModelProperty(HIGHLIGHT_NODE, operationList.get(i).getAddToNode());
                    setModelProperty(HIGHLIGHT_NODE, operationList.get(i).getRemoveFromNode());
                }
            }
        }
    }

    private void operationUnHighlightNode(MyDefaultMutableTreeNode node) {
        setModelProperty(UN_HIGHLIGHT_NODE, node);
        TestDesignControllerList.newInstance().getTreeController().operationRefreshTree();
        DynamicMapControllerList.newInstance().getDynamicMapTreeController().operationRefreshTree();
    }

    public void operationUnHighlightNode(ComparisonTreeNode node) {
        if (node != null) {
            setModelProperty(UN_HIGHLIGHT_NODE, node);
            operationUnHighlightNode(node.getGuiReference());
            TestDesignControllerList.newInstance().getTreeController().operationRefreshTree();
            DynamicMapControllerList.newInstance().getDynamicMapTreeController().operationRefreshTree();
            LinkedList<Operation> operationList = node.getOperations();
            for (int i = 0; i < operationList.size(); i++) {
                setModelProperty(UN_HIGHLIGHT_NODE, operationList.get(i).getAddToNode());
                setModelProperty(UN_HIGHLIGHT_NODE, operationList.get(i).getRemoveFromNode());
            }
        }
    }

    public ComparisonTreeNode operationGetSelectedSourceNode() {
        return (ComparisonTreeNode) getModelProperty(SELECT_SOURCE_NODE);
    }

    public ComparisonTreeNode operationGetSelectedDestinationNode() {
        return (ComparisonTreeNode) getModelProperty(SELECT_DESTINATION_NODE);
    }

    public void operationSelectDestinationNode(ComparisonTreeNode node) {
        operationUnHighlightNode(operationGetSelectedDestinationNode());
        operationUnHighlightNode(operationGetSelectedSourceNode());
        setModelProperty(SELECT_SOURCE_NODE, null);
        setModelProperty(SELECT_DESTINATION_NODE, node);
        if (node != null) {
            operationSetProperties(node.getUserObject().toString());
            operationHighlightNode(node);
        } else {
            operationSetProperties("");
        }
    }

    private void initialize() {
        ComparisonTreeNode sourceRoot = new ComparisonTreeNode(false);
        ComparisonTreeNode destinationRoot = new ComparisonTreeNode(false);
        CompareGUI view = new CompareGUI(TestDesignControllerList.newInstance().getParentFrame(),
                false,
                sourceRoot,
                destinationRoot);
        CompareGUIModel model = new CompareGUIModel();
        addView(view);
        addModel(model);
        view.pack();
    }

    public void preCloseOperations() {
        ComparisonTreeNode dest = operationGetSelectedDestinationNode();
        ComparisonTreeNode sorc = operationGetSelectedSourceNode();
        if (dest != null) {
            operationUnHighlightNode(dest.getGuiReference());
        }
        if (sorc != null) {
            operationUnHighlightNode(sorc.getGuiReference());
        }
    }

    public void operationCloseWindow() {
        cleanAll();
    }

    private ComparisonTreeNode getSource() {
        return (ComparisonTreeNode) getModelProperty(SOURCE);
    }

    private ComparisonTreeNode getDestination() {
        return (ComparisonTreeNode) getModelProperty(DESTINATION);
    }

    private void operationRefreshSourceTree() {
        setModelProperty(SRC_TREE_SRC_REFRESH, true);
    }

    private void operationRefreshDestinationTree() {
        setModelProperty(DST_TREE_REFRESH, true);
    }

    public void operationSelectedMode(String mode) {
        setModelProperty(SELECT_MODE, mode);
    }

    public void operationSetProperties(String text) {
        setModelProperty(PROPERTIES, text);
    }
}
